//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2011 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_ITERATOR_WRAP_H_
#define _BUOLA_ITERATOR_WRAP_H_

#include <buola/iterator/facade.h>
#include <buola/iterator/range.h>

namespace buola {

///\addtogroup iterators
///@{

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///
/// iterator wrap adaptor
///
/// Adapts another iterator (the underlying iterator) so that, when dereferencing, it returns the underlying iterator.
///
/// \tparam tSub the underlying iterator type
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tSub>
class IWrap : public IFacade<IWrap<tSub>,typename std::iterator_traits<tSub>::iterator_category,const tSub,const tSub&,
                             typename std::iterator_traits<tSub>::difference_type>
{
    typedef IFacade<IWrap<tSub>,typename std::iterator_traits<tSub>::iterator_category,const tSub,const tSub&,
                             typename std::iterator_traits<tSub>::difference_type> TFacade;
    friend TFacade;

public:
    ///default constructor (invalid iterator)
    IWrap()=default;

    ///initializes the iterator from an underlying iterator \p pSub
    explicit IWrap(const tSub &pSub)
        :   mSub(pSub)
    {}

    ///allows access to the underlying iterator
    const tSub &Sub()   {   return mSub;    }

private:
    ///returns the underlying iterator
    const tSub& Dereference() const
    {
        return mSub;
    }

    ///increments the underlying iterator
    void Increment()
    {
        ++mSub;
    }

    ///decrements the underlying iterator
    void Decrement()
    {
        --mSub;
    }

    ///advances the underlying iterator by \p pN
    void Advance(typename std::iterator_traits<tSub>::difference_type pN)
    {
        mSub+=pN;
    }

    ///returns true if the two underlying iterators contain the same value
    ///
    ///It is OK to use a generic template parameter, since it will only be called for compatible iterators.
    template<typename tO>
    bool Equal(const tO &pO) const
    {
        return mSub==pO.mSub;
    }

    ///returns the distance to some other underlying iterator
    ///
    ///It is OK to use a generic template parameter, since it will only be called for compatible iterators.
    template<typename tO>
    typename std::iterator_traits<tSub>::difference_type DistanceTo(const tO &pO) const
    {
        return pO.mSub-mSub;
    }

    tSub mSub;
};

///\name Wrap
//@{

///returns an \ref IWrap adapted iterator which returns the underlying iterator
///
///\param pSub the underlying iterator
template<typename tSub>
inline IWrap<tSub> wrap_iterator(const tSub &pSub)
{
    return IWrap<tSub>(pSub);
}

///returns an \ref IWrap object pointing to the beginning of the range \p pRange
///
///\param pRange the range to adapt
///
///It is equivalent to `wrap_iterator(range_begin(pRange))`
template<typename tRange>
inline IWrap<ORangeIterator<tRange>> wrap_begin(const tRange &pRange)
{
    return IWrap<ORangeIterator<tRange>>(range_begin(pRange));
}

///returns an \ref IWrap object pointing past the end of the range \p pRange
///
///\param pRange the range to adapt
///
///It is equivalent to `wrap_iterator(range_end(pRange))`
template<typename tRange>
inline IWrap<ORangeIterator<tRange>> wrap_end(const tRange &pRange)
{
    return IWrap<ORangeIterator<tRange>>(range_end(pRange));
}

///returns a range that can be used to wrap another range
///
///\param pB the beginning of the original range
///\param pE the end of the original range
///
///The range will contain the elements resulting from applying pFunc by the elements in the range [\p pB,\p pE)
///
///it is equivalent to `range(wrap_iterator(pB),wrap_iterator(pE))`
template<typename tSub>
inline CRange<IWrap<tSub>> wrap_range(const tSub &pB,const tSub &pE)
{
    return {IWrap<tSub>(pB),IWrap<tSub>(pE)};
}

///returns a range that can be used to wrap another range
///
///\param pRange
template<typename tRange>
inline auto wrap_range(tRange &pRange) -> decltype(wrap_range(range_begin(pRange),range_end(pRange)))
{
    return wrap_range(range_begin(pRange),range_end(pRange));
}

//@}

///@}

/*namespace buola*/ }

#endif
